%%% Handles socket connections, and bridges a remote server
%%% With a progressquest game.
-module(sockserv_serv).
-behaviour(gen_server).

-record(state, {socket}). %the current socket

-export([start_link/1]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
         code_change/3, terminate/2]).

-define(TIME, 800).
-define(EXP, 50).

%% The socket is passed in from sockserv_sup.
%% It's a listen socket, as started by gen_tcp:listen/2.
%%
%% In Erlang, a TCP socket must be started as a listening socket first.
%% The listening socket can then be used to listen for a connection,
%% meant to be accepted. To do so, use gen_tcp:accept/1-2, as it is done
%% later in this module.
%%
%% A single listen socket can be used by many processes, each accepting
%% a communication. When a communication is accepted with accept/1-2,
%% a new socket, called accept socket, is returned. This accept socket
%% is the one that may be used to communicate with a client.
start_link(Socket) ->
    gen_server:start_link(?MODULE, Socket, []).

init(Socket) ->
    %% Because accepting a connection is a blocking function call,
    %% we can not do it in here. Forward to the server loop!
    gen_server:cast(self(), accept),
    {ok, #state{socket=Socket}}.

handle_call(_E, _From, State) ->
    {noreply, State}.

%% Accepting a connection
handle_cast(accept, S = #state{socket=ListenSocket}) ->
    %% this is the socket acceptance mentioned earlier
    {ok, AcceptSocket} = gen_tcp:accept(ListenSocket),
    %% Remember that thou art dust, and to dust thou shalt return.
    %% We want to always keep a given number of children in this app.
    sockserv_sup:start_socket(), % a new acceptor is born, praise the lord
    io:fwrite("sockserv_serv: New connection!\n"),
    
    %% Start a store worker, my own slave!
    {ok,Worker} = store_sup:start_worker(), 
    io:format("My Worker ~w~n",[Worker]),    
    %% Read from socket as long as its open
    handle_choice(AcceptSocket,Worker),
    % Socket was closed by client
    % Kill both store worker and socket worker
    gen_server:cast(Worker,{stop}), 
    {stop, normal, S}.


%handle_info({tcp, _Port, Msg}, S) ->
%    io:fwrite("Messsage: ~p\n",[Msg]),
%    io:fwrite("handle_info: Close tcp!\n"),
%    gen_tcp:close(S#state.socket),
%    {stop, normal, S};
%handle_info({tcp_closed, _Socket, _}, S) ->
%    {stop, normal, S};
%handle_info({tcp_error, _Socket, _}, S) ->
%    {stop, normal, S};
handle_info(E, S) ->
    io:format("unexpected: ~p~n", [E]),
    {noreply, S}.

code_change(_OldVsn, State, _Extra) ->
    {ok, State}.

terminate(normal, _State) ->
    ok;
terminate(_Reason, _State) ->
    io:format("terminate reason: ~p~n", [_Reason]).
    
%% a "main menu" in which connecting users
%% enter what they want do put/get
handle_choice(Socket,Worker) ->
    case erlang:port_info(Socket) of
        undefined -> ok;
        _ ->
            { Code, Key, Value } = clientiface:get_message(Socket),
            case handle_message(Socket, Worker, Code, Key, Value) of
                ok ->
                    handle_choice(Socket, Worker);
                closed -> 
                    gen_tcp:close(Socket),
                    ok
            end
    end.

%% receives a key and a value, sends OK
handle_message(Socket, Worker, "PUT", Key, Value) ->
   gen_server:call(Worker, {put, Key, Value}),
   clientiface:reply_put_ok(Socket,Key),
   ok;

%% receives a key and returns a value
handle_message(Socket, Worker,"GET", Key, _Value) ->
   Reply = gen_server:call(Worker, {get, Key}),
   case Reply of
      error -> clientiface:reply_get_error(Socket, Key);
      {_,StoredValue} -> clientiface:reply_get_ok(Socket, Key, StoredValue)
   end,
   ok;

handle_message(Socket, _Worker,"DEL", _Key, _Value) ->
   %%gen_server:call(Worker, {del, Key}),
   clientiface:reply_del_ok(Socket, ""),
   ok;

handle_message(_Socket, _W, error_socket_closed, _Key, _Value) ->
   io:fwrite("sockserv_serv: Socket closed. \n"),
   closed;

handle_message(_Socket, _W, Msg, _Key, _Value) ->
   io:fwrite("sockserv_serv: Received unsupported message:" ++ Msg),
   ok.


